package com.freecloud.spring.formework.webmvc.servlet;


import com.freecloud.spring.formework.annotation.FCController;
import com.freecloud.spring.formework.annotation.FCRequestMapping;
import com.freecloud.spring.formework.context.FCApplicationContext;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @Author: maomao
 * @Date: 2019-12-13 11:43
 */
@Slf4j
public class FCDispatcherServlet extends HttpServlet {

    private final String CONTEXT_CONFIG_LOCATION = "contextConfigLocation";

    private FCApplicationContext context;

    private List<FCHandlerMapping> handlerMappings = new ArrayList<>();

    private Map<FCHandlerMapping,FCHandlerAdapter> handlerAdapters = new HashMap<>();

    private List<FCViewResolver> viewResolvers = new ArrayList<>();


    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            this.doDespatch(req,resp);
        }catch (Exception e){
            //new FCModelAndView("500");
            resp.getWriter().write("500 Exception ,Details: \r\n " + Arrays.toString(e.getStackTrace()) );
            log.error("error : {}",e);
        }

    }

    private void doDespatch(HttpServletRequest req, HttpServletResponse resp) throws InvocationTargetException, IllegalAccessException, IOException {
        // 1. 从request拿到url，去匹配

        FCHandlerMapping handler = getHandler(req);
        if(handler == null){
            processDispatchResult(req,resp,new FCModleAndView("404"));
            return ;
        }

        // 2. 调用前，准备好参数
        FCHandlerAdapter ha = getHandlerAdapter(handler);

        // 3. 真正调用方法，返回ModelAndView存储了要传到页面的值，和页面模板名称
        FCModleAndView mv = ha.handler(req,resp,handler);

        // 4. 输出结果
        processDispatchResult(req,resp,mv);
    }

    private void processDispatchResult(HttpServletRequest req, HttpServletResponse resp, FCModleAndView mv) throws IOException {
        //把ModelAndView转换成一个html\ouputStream\json\xml\freemark\veolcity
        //通过contextType判断
        if(mv == null){
            return;
        }

        if(this.viewResolvers.isEmpty()){
            return;
        }

        for (FCViewResolver viewResolver : viewResolvers) {

            FCView view = viewResolver.resolveViewName(mv.getViewName(),null);
            view.render(mv.getModel(),req,resp);

        }


    }

    private FCHandlerAdapter getHandlerAdapter(FCHandlerMapping handler) {
        if(this.handlerAdapters.isEmpty()){
            return null;
        }
        FCHandlerAdapter ha = handlerAdapters.get(handler);
        if(ha.supports(handler)){
            return ha;
        }
        return null;
    }

    private FCHandlerMapping getHandler(HttpServletRequest request){
        if(this.handlerMappings.isEmpty()){
            return null;
        }

        String url = request.getRequestURI();
        String contextPath = request.getContextPath();
        url = url.replace(contextPath,"").replaceAll("/+","/");

        for (FCHandlerMapping handlerMapping : handlerMappings) {
            Matcher matcher = handlerMapping.getPattern().matcher(url);
            if(!matcher.matches()){
                continue;
            }
            return handlerMapping;
        }
        return null;
    }

    @Override
    public void init(ServletConfig config) throws ServletException {
        //1. 初始化ApplicationContext
        context = new FCApplicationContext(config.getInitParameter(CONTEXT_CONFIG_LOCATION));
        //2. 初始化spring mvc 9大组件
        initStrategies(context);

    }

    /**
     * 初始化
     * @param context
     */
    protected void initStrategies(FCApplicationContext context){
        //多文件上传的组件
        //initMultipartResolver(context);

        //初始化本地语言环境
        //initLocaleResolver(context);

        //初始化模板处理器
        //initThemeResolver(context);

        //handlerMapping
        initHandlerMapping(context);
        
        //初始化参数适配器
        initHandlerAdapters(context);
        
        //初始化异常拦截器
        initHandlerExceptionResolvers(context);
        
        //初始化视图预处理器
        initRequestToViewNameTranslator(context);
        
        //初始化视图转换器
        initViewResolvers(context);
        
        //参数缓存器
        initFlashMapManager(context);
    }

    private void initFlashMapManager(FCApplicationContext context) {
    }

    private void initViewResolvers(FCApplicationContext context) {

        //拿到模板配置
        String templateRoot = context.getConfig().getProperty("templateRoot");
        String templateRootPath = this.getClass().getClassLoader().getResource(templateRoot).getFile();

        File templateRootDir = new File(templateRootPath);
        for (File template : templateRootDir.listFiles()) {
            this.viewResolvers.add(new FCViewResolver(templateRoot));
        }

    }

    private void initRequestToViewNameTranslator(FCApplicationContext context) {
    }

    private void initHandlerExceptionResolvers(FCApplicationContext context) {
    }

    /**
     * 把request请求转换为handler，参数进行转换
     *
     * 要拿到handlerMapping才能转换
     *
     * @param context
     */
    private void initHandlerAdapters(FCApplicationContext context) {

        if(this.handlerMappings.isEmpty()){
            log.error("handlerMappings is empty !!!");
            return;
        }

        for (FCHandlerMapping handlerMapping : this.handlerMappings) {
            this.handlerAdapters.put(handlerMapping,new FCHandlerAdapter());
        }


    }

    private void initHandlerMapping(FCApplicationContext context) {
        String[] beanNames = context.getBeanDefinitionNames();
        try{
            for (String beanName : beanNames) {
                Object controller = context.getBean(beanName);
                Class<?> clazz = controller.getClass();
                if(!clazz.isAnnotationPresent(FCController.class)){
                    continue;
                }

                String baseUrl = "";
                //获取controller的URL配置
                if(clazz.isAnnotationPresent(FCRequestMapping.class)){
                    FCRequestMapping requestMapping = clazz.getAnnotation(FCRequestMapping.class);
                    baseUrl = requestMapping.value();
                }

                Method[] methods = clazz.getMethods();
                for (Method method : methods){
                    if(!method.isAnnotationPresent(FCRequestMapping.class)){
                        continue;
                    }
                    FCRequestMapping requestMapping = method.getAnnotation(FCRequestMapping.class);

                    String regex = ("/" + baseUrl + "/" + requestMapping.value().replaceAll("\\*",".*")).replaceAll("/+","/");
                    Pattern pattern = Pattern.compile(regex);

                    handlerMappings.add(new FCHandlerMapping(pattern,controller,method));

                    log.info("mapping " + regex + " init !!");
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }

    }

    private void initThemeResolver(FCApplicationContext context) {
    }

    private void initLocaleResolver(FCApplicationContext context) {
    }

    private void initMultipartResolver(FCApplicationContext context) {
    }
}
